package lk.icta.common.web;

import lk.icta.common.AppConstants;
import lk.icta.common.dao.*;
import lk.icta.common.entity.*;
import lk.icta.common.web.util.MigrationTool;
import lk.icta.core.dao.AlienationRailwayDAO;
import lk.icta.core.dao.GeologicalTypeDAO;
import lk.icta.core.dao.LandRailwayDAO;
import lk.icta.core.dao.LandUsageDAO;
import lk.icta.core.dto.LandRailwayDetails;
import lk.icta.core.entity.*;
import lk.icta.core.service.AlienationRailwayManagementService;
import lk.icta.core.service.LandRailwayManagementService;
import lk.icta.core.service.RailwayPaymentManagementService;
import lk.icta.user.dao.UserDAO;
import lk.icta.user.entity.User;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.util.*;

/**
 * @author Supun Viraj Rathnayaka
 */

public class JSONRailwayLandLookupService extends HttpServlet {

    private static final Logger logger = LoggerFactory.getLogger(JSONRailwayLandLookupService.class);
    private final ObjectMapper mapper = new ObjectMapper();

    private LandRailwayDAO landRailwayDAO;
    private AlienationRailwayDAO alienationRailwayDAO;
    private UserDAO userDAO;
    private BranchDAO branchDAO;
    private GeologicalTypeDAO geologicalTypeDAO;
    private DistrictDAO districtDAO;
    private DistrictEngineerZoneDAO districtEngineerZoneDAO;
    private RailwayTrackDAO railwayTrackDAO;
    private IPWZoneDAO ipwZoneDAO;
    private RailwayProtectionZoneDAO railwayProtectionZoneDAO;
    private RailwayStationDAO railwayStationDAO;
    private LandUsageDAO landUsageDAO;
    private LandRailwayManagementService landRailwayManagementService;
    private RailwayPaymentManagementService paymentManagementService;
    private AlienationRailwayManagementService alienationRailwayManagementService;


    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext());

        landRailwayDAO = (LandRailwayDAO) context.getBean("landRailwayDAO");
        alienationRailwayDAO = (AlienationRailwayDAO) context.getBean("alienationRailwayDAO");
        userDAO = (UserDAO) context.getBean("userDAO");
        branchDAO = (BranchDAO) context.getBean("branchDAO");
        geologicalTypeDAO = (GeologicalTypeDAO) context.getBean("geologicalTypeDAO");
        districtDAO = (DistrictDAO) context.getBean("districtDAO");
        districtEngineerZoneDAO = (DistrictEngineerZoneDAO) context.getBean("districtEngineerZoneDAO");
        railwayTrackDAO = (RailwayTrackDAO) context.getBean("railwayTrackDAO");
        ipwZoneDAO = (IPWZoneDAO) context.getBean("ipwZoneDAO");
        railwayProtectionZoneDAO = (RailwayProtectionZoneDAO) context.getBean("railwayProtectionZoneDAO");
        railwayStationDAO = (RailwayStationDAO) context.getBean("railwayStationDAO");
        landUsageDAO = (LandUsageDAO) context.getBean("landUsageDAO");
        landRailwayManagementService = (LandRailwayManagementService) context.getBean("landRailwayManagementService");
        paymentManagementService = (RailwayPaymentManagementService) context.getBean("railwayPaymentManagementService");
        alienationRailwayManagementService = (AlienationRailwayManagementService) context.getBean("alienationRailwayManagementService");
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String id = request.getParameter(WebConstants.ID);
        String mode = request.getParameter(WebConstants.MODE);
        String type = request.getParameter(WebConstants.TYPE);
        HashMap<String, Object> optionLists = new HashMap<String, Object>();
        try {
            logger.debug("Land lookup");
            HttpSession session = request.getSession();
            User user = (User) session.getAttribute(AppConstants.SESSION_USER_BEAN);
            Locale locale = (Locale) session.getAttribute(WebConstants.SESSION_USER_LANG);
            String language = locale.getLanguage();
            int count = 0;

            if (mode.equals("byUserRail")) {
                logger.debug("byUserRail");
                String pageNo = request.getParameter("pageNo");
                int showMigs = Integer.parseInt(request.getParameter("showMigs"));
                EnumSet<AlienationRailway.State> stateSet = null;
                if (user.getRole().getId().equals(AppConstants.SC_USER_ROLE)) {
                    //optionLists.put("landRailwayDetailList", populateLandRailwayDetails(landRailwayDAO.getLandsByDistrictAndState(Integer.parseInt(id), LandRailway.State.DATA_ENTRY,user)));

                    stateSet = EnumSet.of(AlienationRailway.State.DATA_ENTRY,
                            AlienationRailway.State.REJECTED_BY_BO);


                    optionLists.put("landRailwayDetailList", populateLandRailwayDetailsByAlienation(alienationRailwayDAO.getAlienationRailwayByStateSet(user, stateSet, Integer.parseInt(pageNo), showMigs ,AppConstants.MAX_ROWS_PER_PAGE)));
                } else if (user.getRole().getId().equals(AppConstants.BO_USER_ROLE)) {
                    //optionLists.put("landRailwayDetailList", populateLandRailwayDetails(landRailwayDAO.getLandsByDistrictAndState(Integer.parseInt(id), LandRailway.State.PENDING,user)));

                    stateSet = EnumSet.of(AlienationRailway.State.DATA_ENTRY,
                            AlienationRailway.State.REJECTED_BY_SH);

                    optionLists.put("landRailwayDetailList", populateLandRailwayDetailsByAlienation(alienationRailwayDAO.getAlienationRailwayByStateSet(user, stateSet, Integer.parseInt(pageNo), showMigs, AppConstants.MAX_ROWS_PER_PAGE)));

                } else if (user.getRole().getId().equals(AppConstants.SH_USER_ROLE)) {
                    //optionLists.put("landRailwayDetailList", populateLandRailwayDetails(landRailwayDAO.getLandsByDistrictAndState(Integer.parseInt(id), LandRailway.State.PENDING,user)));

                    stateSet = EnumSet.of(AlienationRailway.State.APPROVED_BY_BO,
                            AlienationRailway.State.REJECTED_BY_SDH);

                    optionLists.put("landRailwayDetailList", populateLandRailwayDetailsByAlienation(alienationRailwayDAO.getAlienationRailwayByStateSet(user, stateSet, Integer.parseInt(pageNo), showMigs, AppConstants.MAX_ROWS_PER_PAGE)));
                } else if (user.getRole().getId().equals(AppConstants.SDH_USER_ROLE)) {
                    //optionLists.put("landRailwayDetailList", populateLandRailwayDetails(landRailwayDAO.getLandsByDistrictAndState(Integer.parseInt(id), LandRailway.State.PENDING,user)));

                    stateSet = EnumSet.of(AlienationRailway.State.APPROVED_BY_SH,
                            AlienationRailway.State.REJECTED_BY_DH);

                    optionLists.put("landRailwayDetailList", populateLandRailwayDetailsByAlienation(alienationRailwayDAO.getAlienationRailwayByStateSet(user, stateSet, Integer.parseInt(pageNo), showMigs, AppConstants.MAX_ROWS_PER_PAGE)));
                } else if (user.getRole().getId().equals(AppConstants.DH_USER_ROLE)) {
                    //optionLists.put("landRailwayDetailList", populateLandRailwayDetails(landRailwayDAO.getLandsByDistrictAndState(Integer.parseInt(id), LandRailway.State.PENDING,user)));

                    stateSet = EnumSet.of(AlienationRailway.State.APPROVED_BY_SDH);

                    optionLists.put("landRailwayDetailList", populateLandRailwayDetailsByAlienation(alienationRailwayDAO.getAlienationRailwayByStateSet(user, stateSet, Integer.parseInt(pageNo), showMigs, AppConstants.MAX_ROWS_PER_PAGE)));

                }
                if (stateSet != null) {
                    count = alienationRailwayDAO.getAllAlienationRailwaysCountByState(user, stateSet);
                }
                optionLists.put("count", count);
                logger.debug("Land lookup by byUserRail [Number of lands: {}]", optionLists.size());


            } else if (type.equals("rail_payment")) {
                if (mode.equals("byLand")) {

                } else if (mode.equals("byLease")) {
                    String l_no = request.getParameter("id");
                    optionLists.put("landRailwayDetailList", populateLandRailwayDetailsByAlienation(alienationRailwayDAO.getAlienationRailwayByLeaseId(user, l_no)));
                }
            } else if (mode.equals("mig")) {
                migrateTool();
            } else if (type.equals("IncomeReport")) {

            } else if (type.equals("railwayLandStat")) {
                Date fromDate;
                Date toDate;
                try {
                    fromDate = new Date(Long.parseLong(request.getParameter("fromDate")));
                    toDate = new Date(Long.parseLong(request.getParameter("toDate")));
                } catch (NumberFormatException e) {
                    fromDate = null;
                    toDate = null;
                }
                int landStateId = Integer.parseInt(request.getParameter("landState"));
                LandRailway.State landState = null;
                if (landStateId > 0) {
                    landState = LandRailway.State.values()[landStateId - 1];
                }

                int migStateId = Integer.parseInt(request.getParameter("migState"));
                LandRailway.MigrationState migState = null;
                if (migStateId > 0) {
                    migState = LandRailway.MigrationState.values()[migStateId - 1];
                }

                // int migState = Integer.parseInt(request.getParameter("migState"));

                //optionLists.put("landList", landManagementService.getLandStats(Integer.parseInt(id), getSearchBy(mode, id), landState, fromDate, toDate, migState));
                optionLists.put("landList", landRailwayManagementService.getLandStats(Integer.parseInt(id), getSearchBy(mode, id), landState, fromDate, toDate, migState));
                logger.debug("Land Statistic [Number of lands: {}]", optionLists.size());
            }
        } catch (Exception e) {
            logger.error("Fatal Error : {}", e);
            return;
        }
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        try {
            mapper.writeValue(out, optionLists);
            out.flush();
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    private String getSearchBy(String mode, String id) {   //byBranch  byRailwayTrack  byDistrict  byAll
        String searchBy = null;
        if (mode.equals("byAll")) {
            logger.debug("Land lookup by All");
            /*if (Integer.parseInt(id) == 0) {
                logger.debug("Land lookup by All");
            } else {
                searchBy = " l.gnDivision.dsDivision.district.province.id = :divisionId";
                logger.debug("Land lookup by Province");
            }*/
        } else if (mode.equals("byDistrict")) {
            searchBy = " l.district.id = :divisionId";
            logger.debug("Land lookup by District");
        } else if (mode.equals("byBranch")) {
            searchBy = " l.branch.id = :divisionId";
            logger.debug("Land lookup by byBranch");
        } else if (mode.equals("byRailwayTrack")) {
            searchBy = " l.railwayTrack.id = :Id";
            logger.debug("Land lookup by byRailwayTrack");
        }
        return searchBy;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    private void migrateTool() {

        String csvFile = "/home/it-front/dev/eRailDocuments/eclipseProjects/eRailMigration/csv/exc_lands_b.csv";
        BufferedReader br = null;
        String line = "";
        String cvsSplitBy = ";";
        LandRailway land;
        AlienationRailway lease;
        LandLocation ll;

        try {
            br = new BufferedReader(new FileReader(csvFile));
            User user = userDAO.getUserByUserName(AppConstants.SYSTEM_USER);
            RailwayTrack track;
            RailwayStation station;
            DistrictEngineerZone deZone;
            RailwayProtectionZone rpZone = railwayProtectionZoneDAO.getRPZoneById(1);
            IPWZone ipwZone = ipwZoneDAO.getIPWZoneById(1);
            boolean isExisting = false;
            Branch b = branchDAO.getBranchById(4);
            int y = 0, z = 0;
            while ((line = br.readLine()) != null) {
                if (y == 0) {
                    y++;
                    continue;
                }
                String[] r = line.split(cvsSplitBy);
                lease = new AlienationRailway();
                logger.info("Processing File No : {}", r[3]);

                List<LandRailway> l = landRailwayDAO.getLandByFileNumber(r[3]);

                if (l.size() > 0) {
                    isExisting = true;

                    if (l.size() == 1) {
                        land = l.get(0);
                    } else {
                        land = l.get(0);
                        logger.warn("Duplicated Land Entries for File No : {}", r[3]);
                    }
                    lease = alienationRailwayDAO.getActiveAlienationByLandId(land.getId(), user);
                } else {
                    isExisting = false;
                    land = new LandRailway();

                }
                track = railwayTrackDAO.getRailwayTrackById(MigrationTool.getRailwayTrackId(r[0]));
                station = railwayStationDAO.getRailwayStationByCode(MigrationTool.getCleanStringUpperCase(r[23]));
                deZone = districtEngineerZoneDAO.getDistrictEngineerZoneById(MigrationTool.getDeZoneId(r[13]));
                if (track == null) {
                    if (station != null) {
                        track = station.getRailwayTrack();
                    } else {
                        continue;
                    }
                }

                if (deZone == null) {
                    deZone = districtEngineerZoneDAO.getDistrictEngineerZoneById(1);
                }

                if (station != null) {
                    ipwZone = station.getIpwZone();
                    rpZone = station.getRailwayProtectionZone();
                }


                land.setLandFileNumber(MigrationTool.getFileNo(r[3]));
                land.setBranch(b);
                land.setState(LandRailway.State.DATA_ENTRY);
                land.setMigrationState(LandRailway.MigrationState.EXCEL);
                land.setGeologicalType(geologicalTypeDAO.getGeologicalTypeById(1, user));
                land.setDistrict(districtDAO.getDistrictById(MigrationTool.getDistrictId(r[7])));
                land.setDistrictEngineerZone(deZone);

                // Location Data
                ll = new LandLocation();
                ll.setFromMile(MigrationTool.getMilegeValue(r[9]));
                ll.setFromChain(MigrationTool.getMilegeValue(r[10]));
                ll.setToMile(MigrationTool.getMilegeValue(r[11]));
                ll.setFromChain(MigrationTool.getMilegeValue(r[12]));

                land.setLandLocation(ll);

                land.setRailwayTrack(track);
                land.setPlanNumber(MigrationTool.getPlanNo(r[4]));
                land.setPlanDate(MigrationTool.getDate(r[5]));
                land.setSurveyor(MigrationTool.getServeyorName(r[6]));
                land.setVillage(MigrationTool.getVillage(r[8]));
                land.setIpwZone(ipwZone);
                land.setRailwayProtectionZone(rpZone);
                land.setNearestRailwayStation(station);
                land.setNorthBoundary(r[25].trim());
                land.setEastBoundary(r[27].trim());
                land.setSouthBoundary(r[26].trim());
                land.setWestBoundary(r[28].trim());
                land.setLandExtent(MigrationTool.getAreaInPerchs(r[19].trim(), r[20].trim(), r[21].trim()));

                land.getLifeCycleInfo().setCreatedUser(user);
                land.getLifeCycleInfo().setLastUpdatedUser(user);
                land.getLifeCycleInfo().setCreatedTimeStamp(new Date());
                land.getLifeCycleInfo().setLastUpdatedTimeStamp(new Date());

                lease.setApproveState(AlienationRailway.State.DATA_ENTRY);
                //lease.setLand(land);
                lease.setValuation(new Valuation());
                lease.setMigrationState(AlienationRailway.MigrationState.EXCEL);
                lease.setLeaseUsagePrimary(landUsageDAO.getLandUsageById(MigrationTool.getUsageId(r[16]), user));
                lease.setLeaseUsageSecondary(landUsageDAO.getLandUsageById(MigrationTool.getUsageId(r[17]), user));
                lease.setReferenceNo(MigrationTool.getCleanStringUpperCase(r[1]));

                Holder holder = new Holder();
                holder.setName((r[30].trim().equals("")) ? "Holder Name Missing" : r[30].trim());
                Contacts c = new Contacts();
                c.setAddress(r[31].trim());
                holder.setContacts(c);

                LeaseDetail detail = new LeaseDetail();
                //detail.setLeaseNo(MigrationTool.getCleanStringUpperCase(r[1]));
                detail.setValidityPeriod(MigrationTool.getValidityPeriod(r[33]));
                detail.setLeaseStartingDate(MigrationTool.getDate(r[32]));
                detail.setAnnualRental(MigrationTool.getValuationAmount(r[34]));
                detail.setPaidAmount(MigrationTool.getValuationAmount(r[37]));
                detail.setAnnualTax(0d);
                detail.setPaidTax(0d);

                lease.setHolder(holder);
                lease.setLeaseDetail(detail);

                Valuation v = new Valuation();
                v.setAddedPercentage(MigrationTool.getAddedPercentage(r[36]));
                v.setLastValuationPeriodStart(MigrationTool.getDate(r[35]));
                v.setValuationAmount(MigrationTool.getValuationAmount(r[34]));
                v.setValuationOption(false);

                lease.setValuation(v);

                Payment p = new Payment();

                p.setPaymentType(Payment.PaymentType.UNIDENTIFIED.ordinal());
                p.setPaymentState(Payment.PaymentState.INVOICED);
                p.setReceiptReferenceNo("MIG " + lease.getReferenceNo());
                p.setBillingDate(new Date());
                p.setPaymentDate(new Date());
                p.setDescription("Migrated Payment Data");
                p.setRentalAmount(detail.getPaidAmount());
                p.setRentalAmount(0);


                lease.getLifeCycleInfo().setCreatedUser(user);
                lease.getLifeCycleInfo().setLastUpdatedUser(user);
                lease.getLifeCycleInfo().setCreatedTimeStamp(new Date());
                lease.getLifeCycleInfo().setLastUpdatedTimeStamp(new Date());
                if (lease.getReferenceNo().equals("")) {
                    lease.setReferenceNo("TEMP " + z);
                    z++;
                }
                if (isExisting) {
                    landRailwayManagementService.updateLand(land, lease, user, "english");
                } else {
                    landRailwayManagementService.addLand(land, lease, user, "english");
                }

                List<AlienationRailway> a = alienationRailwayManagementService.getAlienationByReferenceNo(lease.getReferenceNo(), user);
                AlienationRailway al = null;
                for (AlienationRailway ar : a) {
                    if (ar.getLeaseDetail() != null) {
                        al = ar;
                        break;
                    }
                }

                paymentManagementService.addPayment(p, al.getId(), user);
                //List<Payment> payments =a.get(0).getPayments();
                a = alienationRailwayManagementService.getAlienationByReferenceNo(lease.getReferenceNo(), user);
                for (AlienationRailway ar : a) {
                    if (ar.getLeaseDetail() != null) {
                        al = ar;
                        break;
                    }
                }
                paymentManagementService.confirmPayment(al.getPayments().get(0).getId(), user);
                //alienationRailwayDAO.addAlienation(lease,user);

                logger.info("Lands Migrating... {}", y);
                y++;
            }
            logger.info("{} Lands Migrated", y);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private List<LandRailwayDetails> populateLandRailwayDetailsByAlienation(List<AlienationRailway> alienationRailways) {
        List<LandRailwayDetails> list = new ArrayList<LandRailwayDetails>();
        for (AlienationRailway alienationRailway : alienationRailways) {
            LandRailwayDetails railwayDetails = new LandRailwayDetails();

            railwayDetails.setId(alienationRailway.getLand().getId());
            railwayDetails.setLandFileNumber(alienationRailway.getLand().getLandFileNumber());
            railwayDetails.setPlanNumber(alienationRailway.getLand().getPlanNumber());
            railwayDetails.setLotNumber(alienationRailway.getLand().getLotNumber());
            railwayDetails.setLandExtent(alienationRailway.getLand().getLandExtent());
            railwayDetails.setDistrictName(alienationRailway.getLand().getDistrict().getEnDistrictName());
            railwayDetails.setMigrationState(alienationRailway.getMigrationState().ordinal());
            railwayDetails.setLeaseNumber(alienationRailway.getReferenceNo());
            list.add(railwayDetails);

        }

        return list;

    }
}
